
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#include <mtd/mtd-abi.h>

#include <stdio.h>
#include <libgen.h>

static void show_info(int fd);
static int get_mem_info(int fd, struct mtd_info_user *info);
static int get_regions(int fd, struct region_info_user *info, int *n);
static void printsize(u_int32_t x)
{
        int i;
        static const char *flags = "KMGT";
        printf("%u ", x);
        for (i = 0; x >= 1024 && flags[i] != '\0'; i++)
                x /= 1024;
        i--;
        if (i >= 0)
                printf("(%u%c)", x, flags[i]);
}

int main(int argc, char *argv[])
{
    int fd = 0;

    if (argc != 2) {
        printf("%s <device>\n", basename(argv[0]));
        return 0;
    }

    fd = open(argv[1], O_SYNC|O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "open %s failed", argv[1]);
        return 0;
    }

    show_info(fd);

    close(fd);

    return 0;
}

inline int get_mem_info(int fd, struct mtd_info_user *info)
{
    return ioctl(fd, MEMGETINFO, info);
}

int get_regions(int fd, struct region_info_user *info, int *n)
{
    int i = 0;
    if (ioctl(fd, MEMGETREGIONCOUNT, n)) {
        return -1;
    }

    for (i=0; i<*n; ++i) {
        info[i].regionindex = i;
        if (ioctl(fd, MEMGETREGIONINFO, &info[i])) {
            return -1;
        }
    }

    return 0;
}

void show_info(int fd)
{
    int ret = 0;
    int regionCount = 0;
    int i = 0;
    struct mtd_info_user mtdInfo;
    struct region_info_user regionInfo[1024];

    ret = get_mem_info(fd, &mtdInfo);
    if (ret < 0) {
        perror("get mem infomation failed");
        return;
    }

    ret = get_regions(fd, regionInfo, &regionCount);
    if (regionCount < 0) {
        perror("get resions infomation failed");
        return;
    }

    printf("mtd.type = ");
    switch (mtdInfo.type) {
        case MTD_ABSENT:
            printf("MTD_ABSENT");
            break;
        case MTD_RAM:
            printf("MTD_RAM");
            break;
        case MTD_ROM:
            printf("MTD_ROM");
            break;
        case MTD_NORFLASH:
            printf("MTD_NORFLASH");
            break;
        case MTD_NANDFLASH:
            printf("MTD_NANDFLASH");
            break;
        case MTD_DATAFLASH:
            printf("MTD_DATAFLASH");
            break;
        case MTD_UBIVOLUME:
            printf("MTD_UBIVOLUME");
        default:
            printf("(unknown type - new MTD API maybe?)");
    }

    printf("\nmtd.flags = ");
    if (mtdInfo.flags == MTD_CAP_ROM)
        printf("MTD_CAP_ROM");
    else if (mtdInfo.flags == MTD_CAP_RAM)
        printf("MTD_CAP_RAM");
    else if (mtdInfo.flags == MTD_CAP_NORFLASH)
        printf("MTD_CAP_NORFLASH");
    else if (mtdInfo.flags == MTD_CAP_NANDFLASH)
        printf("MTD_CAP_NANDFLASH");
    else if (mtdInfo.flags == MTD_WRITEABLE)
        printf("MTD_WRITEABLE");
    else {
        int first = 1;
        static struct {
            const char *name;
            int value;
        } flags[] =
        {
            { "MTD_WRITEABLE", MTD_WRITEABLE },
            { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE },
            { "MTD_NO_ERASE", MTD_NO_ERASE },
            { "MTD_POWERUP_LOCK", MTD_POWERUP_LOCK },
            { NULL, -1 }
        };

        int i;
        for (i = 0; flags[i].name != NULL; ++i) {
            if (mtdInfo.flags & flags[i].value) {
                if (first) {
                    printf("%s", flags[i].name);
                    first = 0;
                } else {
                    printf(" | %s", flags[i].name);
                }
            }
        }
    }

    printf("\nmtd.size = ");
    printsize(mtdInfo.size);

    printf("\nmtd.erasesize = ");
    printsize(mtdInfo.erasesize);

    printf("\nmtd.writesize = ");
    printsize(mtdInfo.writesize);

    printf("\nmtd.oobsize = ");
    printsize(mtdInfo.oobsize);

    printf("\nregions = %d\n\n", regionCount);

    for (i = 0; i < regionCount; i++) {
        printf("region[%d].offset = 0x%.8x\n"
                "region[%d].erasesize = ",
                i, regionInfo[i].offset, i);
        printsize(regionInfo[i].erasesize);
        printf("\nregion[%d].numblocks = %d\n"
                "region[%d].regionindex = %d\n",
                i, regionInfo[i].numblocks,
                i, regionInfo[i].regionindex);
    }
}
